<!DOCTYPE html>
<html lang="zh-cn">

<head>

<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<meta name="keywords" content="note, math, music, 笔记, 数学, 音乐">
<meta name="description" content="zmx0142857 的个人笔记，包括数学、计算机、物理、语言、音乐等学科">
<meta name="author" content="zmx0142857">
<title>zmx0142857 的数学笔记</title>
<link id="favicon" rel="shortcut icon" href="img/pikachu.png"/>
<!-- 字体预加载 -->
<link rel="prefetch" href="font/latinmodern-math.woff2" as="font" type="font/woff2" crossorigin/>
<link rel="prefetch" href="https://unpkg.com/katex@0.13.0/dist/fonts/KaTeX_Main-Regular.woff2" as="font" type="font/woff2" crossorigin/>
<link rel="prefetch" href="https://unpkg.com/katex@0.13.0/dist/fonts/KaTeX_Math-Italic.woff2" as="font" type="font/woff2" crossorigin/>
<!-- 不常用
<link rel="preload" href="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Math-BoldItalic.woff2" as="font" type="font/woff2" crossorigin/>
<link rel="preload" href="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Size1-Regular.woff2" as="font" type="font/woff2" crossorigin/>
<link rel="preload" href="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Size2-Regular.woff2" as="font" type="font/woff2" crossorigin/>
<link rel="preload" href="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Size3-Regular.woff2" as="font" type="font/woff2" crossorigin/>
<link rel="preload" href="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Size4-Regular.woff2" as="font" type="font/woff2" crossorigin/>
-->
<!-- 首页用到的样式直接内联 -->
<style type="text/css">
* {
  margin: 0;
  padding: 0;
}

body {
  text-rendering: optimizeLegibility;
  font-size: 16px;
  font-family: "-apple-system", "Helvetica Neue", "Roboto", "Segoe UI", sans-serif;
  margin: 0;
}

header {
  height: 45px;
  line-height: 45px;
  font-size: 18px;
  display: flex;
  justify-content: space-between;
  position: absolute;
  width: 100%;
  top: 0;
  left: 0;
  right: 0;
  text-align: center;
  font-weight: 500;
  border-color: #000;
  background-color: #444;
  color: #fff;
  transition: 1s;
}

header a {
  height: 45px;
  width: 45px;
  border: none;
  color: inherit;
  font-size: inherit;
  font-weight: inherit;
  background: rgba(0,0,0,0);
  cursor: pointer;
  text-decoration: none;
}

header a:focus {
  border: none;
}

header a:first-child {
  left: 0;
}

header a:last-child {
  right: 0;
}

header #article-title {
  flex: auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 站内搜索 */
header .search {
  position: absolute;
  right: 45px;
}
#search-box {
  font-size: 14px;
  padding: 8px;
  background: transparent !important;
  border: none !important;
  border-radius: 3px;
  width: 16px;
  transition: width 0.3s;
  color: transparent;
}
#search-box:focus,
#search-box.focus {
  width: 160px;
  background: #333 !important;
  border: 1px solid #555 !important;
  color: #fff;
}
#search-box::-webkit-input-placeholder {
  color: transparent;
}
#search-button {
  color: transparent;
  vertical-align: middle;
  border: none !important;
  background: transparent !important;
  pointer-events: none;
  position: absolute;
  top: 15px;
  right: 10px;
}
#search-box:focus + #search-button,
#search-button:focus {
  pointer-events: unset;
}
#search-button > img {
  width: 18px !important;
  height: 18px !important;
}
#nav {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  width: 0;
  display: flex;
  flex-flow: column nowrap;
  overflow-y: hidden;
  background: #fcfcfc;
  /*box-shadow: inset -14px 0px 5px -12px rgb(210,210,210);*/
  border-right: 1px #444;
  transition: .5s;
  z-index: 0;
}

#table-of-contents {
  margin-top: 45px;
  height: 100%;
  overflow: auto;
  list-style-type: none;
  color: #259;
  box-shadow: inset -14px 0px 5px -12px rgb(210,210,210);
}

#table-of-contents ul {
  list-style-type: none;
}

#table-of-contents li {
  text-indent: 1em;
  line-height: 2em;
  /* both of the following are required for text-overflow */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

#table-of-contents ul li {
  padding-left: 1em;
  color: #444;
/* 折叠目录
  height: 0;
  overflow-y: hidden;
  transition: .3s ease;
}

#table-of-contents > li:hover li {
  height: 2em;
  transition: .3s ease;
*/
}

#table-of-contents ul li:hover,
#table-of-contents ul li.active {
  background: linear-gradient(90deg, #eef 80%, transparent);
  cursor: pointer;
}
li.isnew::after {
  content: '';
  background: #d34;
  border-radius: 50%;
  display: inline-block;
  height: 6px;
  width: 6px;
  position: relative;
  top: -5px;
  left: 3px;
}

#table-of-contents a {
  text-decoration: none;
  color: inherit;
}

#article {
  position: absolute;
  margin-left: 0;
  width: 100%;
  height: 100%;
  transition: .5s;
  z-index: 1;
}

#iframe {
  display: table;
  padding: 0;
  border: 0;
  width: 100%;
  height: 100%;
}

#eclipse {
  height: 100%;
  width: 100%;
  display: none;
  position: absolute;
  top: 0;
}

#yinyang {
  transition: .5s;
}

#book-wrapper {
  display: inline-block;
  position: relative; /* 正确显示箭头和文字的堆叠, 这是关键一步 */
  width: 50%;
  z-index: 1;
}

#book-wrapper::before {
  position: absolute;
  right: 10px;
  top: 18px;
  content: "";
  border: 10px solid transparent;
  border-top-color: white;
  cursor: pointer;
  z-index: -1;
}

#book {
  cursor: pointer;
  border: none;
  border-radius: 4px;
  color: inherit;
  background: transparent;
  background-image: none;
  height: 40px;
  width: 100%;
  font-size: inherit;
  text-align: center;
  -webkit-appearance: none;
  -moz-appearance: none;
}

#book option {
  color: black;
}

#book:focus {
  outline: none;
}

#body-yellow {
  background: #eec;
}

#body-green {
  background: #e4fbe8;
}

#body-purple {
  background: #ddf;
}

#body-dark {
  background: #222;
}

#body-pink {
  background: #ecc;
}

/* mobile phone */
@media screen and (min-resolution: 150dpi) {}

/* iphone */
@media screen and (-webkit-min-device-pixel-ratio:2) {}

#about {
  transition: .3s;
  transform: rotateY(calc(var(--flag) * 180deg));
  --flag: 0;
}
#about > .back {
  display: none;
  position: absolute;
  background: #fff;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 20px;
  text-align: center;
  transform: scaleX(-1);
}
#about.back > .back {
  display: block;
}
#about .img {
  border-radius: 50%;
  background: #eec;
  margin: 10px auto 20px auto;
  height: 80px;
  width: 80px;
}
#about .links {
  font-size: 0.9em;
  color: #888;
  text-align: center;
}
#about a {
  font-size: 0.9em;
  color: #259;
  text-decoration: none;
}
#about hr {
  border-color: #eee;
  margin: 10px 0;
}
#about ul {
  list-style-type: none;
}
.flex-center {
  display: flex;
  justify-content: center;
  align-items: center;
}
::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
::-webkit-scrollbar-thumb {
  background-color: rgba(0,0,0,0.2);
  background-clip: padding-box;
  border-radius: 4px;
  min-height: 28px;
}
::-webkit-scrollbar-track-piece {
  background-color: transparent;
}
</style>

</head>

<body>

<nav id="nav" hidden>
  <!-- Search Engine Optimize -->
<ul id="table-of-contents"><li>示例<ul><li><a href="math/example/editor.html">公式编辑器</a></li><li><a href="math/example/mathml.html">Mathml</a></li><li><a href="math/example/surface.html">曲面在线绘制</a></li></ul></li><li>未分类<ul><li><a href="math/misc/0.html">问题</a></li><li><a href="math/misc/2.html">三角函数</a></li><li><a href="math/misc/3.html">不等式</a></li><li><a href="math/misc/4.html">微积分不等式</a></li><li><a href="math/misc/5.html">因式分解</a></li><li><a href="math/misc/6.html">数学家</a></li><li><a href="math/misc/7.html">算术与进制</a></li></ul></li><li>高等代数<ul><li><a href="math/algebra/0.html">初等代数</a></li><li><a href="math/algebra/1.html">矩阵</a></li><li><a href="math/algebra/2.html">行列式</a></li><li><a href="math/algebra/3.html">向量的相关性与线性方程组</a></li><li><a href="math/algebra/4.html">线性空间</a></li><li><a href="math/algebra/5.html">线性映射与线性变换</a></li><li><a href="math/algebra/6.html">特征值与特征向量</a></li><li><a href="math/algebra/7.html">矩阵的相似标准形</a></li><li><a href="math/algebra/8.html">线性空间中的度量与内积</a></li><li><a href="math/algebra/9.html">实二次型与正定矩阵</a></li><li><a href="math/algebra/10.html">李代数初步</a></li></ul></li><li>数学分析<ul><li><a href="math/analysis/0.html">附录</a></li><li><a href="math/analysis/1.html">初等函数</a></li><li><a href="math/analysis/2.html">数列的极限</a></li><li><a href="math/analysis/3.html">函数的极限与连续性</a></li><li><a href="math/analysis/5.html">导数的应用</a></li><li><a href="math/analysis/6.html">不定积分</a></li><li><a href="math/analysis/7.html">定积分与广义积分</a></li><li><a href="math/analysis/8.html">含参积分与特殊函数</a></li><li><a href="math/analysis/9.html">椭圆函数, 超几何函数</a></li><li><a href="math/analysis/10.html">Bernoulli 数与 Riemann zeta 函数</a></li><li><a href="math/analysis/11.html">常数项级数</a></li><li><a href="math/analysis/12.html">函数列与函数项级数</a></li><li><a href="math/analysis/13.html">Fourier 级数</a></li><li><a href="math/analysis/14.html">多元函数的极限与连续性</a></li><li><a href="math/analysis/15.html">多元函数微分学</a></li><li><a href="math/analysis/17.html">多元函数的极值</a></li><li><a href="math/analysis/19.html">重积分</a></li><li><a href="math/analysis/22.html">场论初步</a></li><li><a href="math/analysis/23.html">分数阶微积分</a></li><li><a href="math/analysis/24.html">函数方程</a></li></ul></li><li>数论<ul><li><a href="math/number/0.html">整数, 有理数, 代数数, 连分数</a></li><li><a href="math/number/1.html">整除, 最大公约数, 素数</a></li><li><a href="math/number/2.html">同余</a></li><li><a href="math/number/3.html">阶与原根</a></li><li><a href="math/number/4.html">二次剩余</a></li><li><a href="math/number/5.html">不定方程</a></li><li><a href="math/number/6.html">积性数论函数</a></li><li><a href="math/number/11.html">代数数域和代数整数</a></li><li><a href="math/number/12.html">理想的分解</a></li></ul></li><li>集合论与点集拓扑<ul><li><a href="math/set/2.html">集合及其运算</a></li><li><a href="math/set/3.html">二元关系与映射</a></li><li><a href="math/set/4.html">自然数</a></li><li><a href="math/set/6.html">基数</a></li></ul></li><li>复变函数<ul><li><a href="math/complex/1.html">复数与复变函数</a></li><li><a href="math/complex/2.html">全纯函数</a></li><li><a href="math/complex/3.html">复变函数的展开</a></li><li><a href="math/complex/6.html">积分变换</a></li></ul></li><li>实变函数<ul><li><a href="math/real/1.html">预备知识</a></li><li><a href="math/real/2.html">Lebesgue 测度</a></li></ul></li><li>概率论<ul><li><a href="math/prob/1.html">事件与概率</a></li><li><a href="math/prob/2.html">随机变量</a></li><li><a href="math/prob/4.html">极限定理</a></li><li><a href="math/prob/5.html">常用分布</a></li></ul></li><li>数理统计<ul><li><a href="math/stat/1.html">数理统计的基本概念</a></li><li><a href="math/stat/2.html">参数估计与假设检验</a></li></ul></li><li>数值分析<ul><li><a href="math/numeric/2.html">插值与样条</a></li><li><a href="math/numeric/5.html">线性方程组</a></li><li><a href="math/numeric/6.html">矩阵</a></li><li><a href="math/numeric/7.html">非线性方程数值解法</a></li><li><a href="math/numeric/8.html">常微分方程数值解法</a></li><li><a href="math/numeric/9.html">偏微分方程的 Ritz-Galerkin 方法</a></li></ul></li><li>泛函分析<ul><li><a href="math/functional/1.html">度量空间</a></li><li><a href="math/functional/2.html">线性赋范空间</a></li><li><a href="math/functional/3.html">有界线性算子</a></li><li><a href="math/functional/4.html">Hilbert 空间及其上的有界线性算子</a></li></ul></li><li>抽象代数<ul><li><a href="math/abstract/2.html">群 (上)</a></li><li><a href="math/abstract/3.html">群 (中)</a></li><li><a href="math/abstract/4.html">群 (下)</a></li><li><a href="math/abstract/5.html">半群</a></li><li><a href="math/abstract/6.html">环</a></li><li><a href="math/abstract/7.html">域</a></li><li><a href="math/abstract/8.html">Galois 理论</a></li></ul></li><li>拓扑学<ul><li><a href="math/topology/1.html">拓扑空间与连续映射</a></li></ul></li><li>常微分方程<ul><li><a href="math/ode/2.html">一阶微分方程的初等解法</a></li><li><a href="math/ode/4.html">高阶微分方程</a></li><li><a href="math/ode/5.html">线性微分方程组</a></li></ul></li><li>偏微分方程<ul><li><a href="math/pde/1.html">方程的导出</a></li><li><a href="math/pde/2.html">波动方程</a></li><li><a href="math/pde/3.html">热传导方程</a></li><li><a href="math/pde/4.html">位势方程</a></li><li><a href="math/pde/5.html">二阶线性偏微分方程的分类</a></li></ul></li><li>平面几何<ul><li><a href="math/geometry/1.html">五组公理</a></li><li><a href="math/geometry/3.html">三角形, 四边形</a></li><li><a href="math/geometry/4.html">圆</a></li><li><a href="math/geometry/5.html">抛物线</a></li></ul></li><li>解析几何<ul><li><a href="math/analytic-geo/1.html">向量与坐标系</a></li><li><a href="math/analytic-geo/2.html">平面与直线</a></li><li><a href="math/analytic-geo/3.html">圆锥曲线与二次曲面</a></li><li><a href="math/analytic-geo/6.html">平面几何的复数解法</a></li></ul></li><li>微分几何<ul><li><a href="math/diff-geo/1.html">欧氏空间</a></li><li><a href="math/diff-geo/2.html">曲线的局部理论</a></li><li><a href="math/diff-geo/3.html">曲面的局部理论</a></li><li><a href="math/diff-geo/4.html">标架与曲面论基本定理</a></li><li><a href="math/diff-geo/5.html">曲面的内蕴几何学</a></li></ul></li><li>运筹学<ul><li><a href="math/or/1.html">线性规划</a></li></ul></li><li>组合数学<ul><li><a href="math/comb/1.html">引言, 计数原理, 鸽巢原理</a></li><li><a href="math/comb/2.html">排列与组合</a></li><li><a href="math/comb/3.html">求解递推式</a></li><li><a href="math/comb/4.html">容斥原理</a></li><li><a href="math/comb/5.html">离散微积分与特殊计数序列</a></li><li><a href="math/comb/6.html">生成函数</a></li></ul></li><li>图论<ul><li><a href="math/graph/2.html">Paths and cycles</a></li></ul></li><li>数学游戏<ul><li><a href="math/recreation/1.html">数学游戏</a></li></ul></li><li>数据结构<ul><li><a href="cs/ds/exercise.html">数据结构习题</a></li><li><a href="cs/ds/0.html">其它算法</a></li><li><a href="cs/ds/1.html">绪论</a></li><li><a href="cs/ds/2.html">线性表</a></li><li><a href="cs/ds/3.html">栈与队列</a></li><li><a href="cs/ds/4.html">递归与回溯</a></li><li><a href="cs/ds/5.html">字符串</a></li><li><a href="cs/ds/6.html">树与二叉树</a></li><li><a href="cs/ds/7.html">矩阵与广义表</a></li><li><a href="cs/ds/8.html">图</a></li><li><a href="cs/ds/9.html">查找</a></li><li><a href="cs/ds/10.html">随机算法</a></li><li><a href="cs/ds/11.html">排序</a></li><li><a href="cs/ds/12.html">文件</a></li><li><a href="cs/ds/13.html">数论算法</a></li><li><a href="cs/ds/14.html">动态规划与贪心算法</a></li><li><a href="cs/ds/15.html">计算几何</a></li><li><a href="cs/ds/16.html">算法题模板</a></li></ul></li><li>机器学习<ul><li><a href="cs/ml/1.html">Logistic 回归</a></li></ul></li><li>操作系统<ul><li><a href="cs/os/1.html">引论</a></li><li><a href="cs/os/2.html">进程</a></li><li><a href="cs/os/3.html">内存</a></li></ul></li><li>计算机组成原理<ul><li><a href="cs/com/1.html">计算机系统概论</a></li></ul></li><li>数据库系统概论<ul><li><a href="cs/db/1.html">绪论</a></li><li><a href="cs/db/2.html">关系数据库</a></li><li><a href="cs/db/3.html">SQL</a></li><li><a href="cs/db/exercise.html">数据库习题</a></li></ul></li><li>计算机网络<ul><li><a href="cs/net/1.html">概述</a></li><li><a href="cs/net/2.html">物理层</a></li></ul></li><li>工具<ul><li><a href="cs/utils/svg-flowchart.html">svg 流程图</a></li><li><a href="cs/utils/exam.html">无奖竞猜</a></li></ul></li><li>未分类<ul><li><a href="physics/misc/1.html">单位与数量</a></li><li><a href="physics/misc/6.html">物理学家</a></li></ul></li><li>力学<ul><li><a href="physics/mechanics/1.html">质点运动学</a></li></ul></li><li>流体力学<ul><li><a href="physics/hydromechanics/1.html">流体力学基础</a></li></ul></li><li>光学<ul><li><a href="physics/optics/1.html">光学</a></li></ul></li><li>汉语<ul><li><a href="lang/zh/words.html">易读错字词整理</a></li></ul></li><li>日语<ul><li><a href="lang/jp/kana.html">学五十音</a></li><li><a href="lang/jp/words.html">日语动词</a></li></ul></li><li>歌词<ul><li><a href="lang/lyric/larc~en~ciel.html">L'arc~en~Ciel</a></li></ul></li><li>工具<ul><li><a href="music/utils/player.html">播放器</a></li><li><a href="music/utils/jianpu.html">简谱</a></li><li><a href="music/utils/lywriter.html">乐谱编辑器</a></li><li><a href="music/utils/ear-training.html">练耳</a></li><li><a href="music/utils/piano.html">键盘</a></li><li><a href="music/utils/svg-score.html">乐谱</a></li></ul></li></ul>
  <header>
    <a id="prev-article">&#x276e;</a>
    <div id="book-wrapper">
      <select name="book" id="book" title="book">
        <!--&#x2304;-->
      </select>
    </div>
    <a id="next-article">&#x276f;</a>
  </header>
</nav>

<article id="article">
  <header id="header">
    <a id="yinyang" title="目录">&#x262f;</a>
    <span id="article-title"></span>
    <span class="search">
      <input id="search-box"><button title="search" id="search-button"></button>
    </span>
    <a id="show-about" title="About" style="font-size:1.5em">&#x2767;</a>
  </header>
  <div id="eclipse"></div>
  <div id="about" style="display:none">
    <div class="img flex-center">
      <img src="img/pikachu.png" alt="头像">
    </div>
    <center>zmx0142857</center>
    <p class="links">
      <a href="mailto:892298182@qq.com">邮箱</a>・<a href="https://github.com/zmx0142857">github</a>・<a href="https://gitee.com/zmx0142857">gitee</a>
    </p>
    <hr>
    <div class="links">
      友情链接:
      <ul>
        <li>數心 <a href="https://matheart.github.io">github</a>・<a href="https://space.bilibili.com/346660989">bilibili</a></li>
        <li>mobtgzhang <a href="https://mobtgzhang.github.io">github</a></li>
      </ul>
    </div>
    <div class="back">
      广告位招租
    </div>
  </div>
</article>

<script src="js/toc.js"></script>
<script src="js/ui.js"></script>
<script>
'use strict';
(function(){

// 时尚卡片
{
  const aboutDlg = new Modal('about')
  $('#show-about').onclick = function () {
    aboutDlg.toggle()
  }

  const about = $('#about').addEventListener('click', function () {
    var degY = parseInt(this.style.getPropertyValue('--flag'))
    degY = !degY ? 1 : 0
    degY ? this.classList.add('back') : this.classList.remove('back')
      this.style.setProperty('--flag', degY)
  }, true) // 向上冒泡
}

function mockClick (el) {
  el.dispatchEvent(new MouseEvent('click', {
    view: window,
    bubbles: true,
    cancelable: true
  }))
}

// 能用就行的搜索框交互
{
  const searchBox = $('#search-box')
  $('#search-button').addEventListener('click', function () {
    if (searchBox.value) {
      searchBox.classList.add('focus')
    } else {
      searchBox.classList.remove('focus')
      // 模拟点击 body, 使得提示框自动关闭
      mockClick(document.body)
    }
    this.blur()
  })
}

// elements
let notify, iframe
const article = $('#article'),
  header = $('#header'),
  nav = $('#nav'),
  yinyang = $('#yinyang'),
  eclipse = $('#eclipse')

header.onmouseover = () => headerVisible(true)
header.onmouseout = () => headerVisible(false)
yinyang.onclick = eclipse.onclick = () => hideNav()

function createIframe(src, title) {
  if (iframe) article.removeChild(iframe)
  // 全新创建的 iframe 不会影响 history
  iframe = $('<iframe>', {
    id: 'iframe',
    title,
    src,
    onload () {
      notify.remove()
      iframe.focus()
    }
  })
  //iframe.contentWindow.handleKeyboard = handleKeyboard;
  article.insertBefore(iframe, header)
  $('#article-title').innerText = title
  document.title = title
}

function hideNav(flag) {
  if (!nav.hidden || flag) {
    nav.hidden = true
    nav.style.width = '0'
    article.style.marginLeft = '0'
    yinyang.style.transform = 'none'
    eclipse.style.display = 'none'
    setTimeout(() => document.body.style.overflowX = 'unset', 500)
    headerVisible(false)
    setTimeout(() => mockClick(iframe), 100)
  } else {
    nav.removeAttribute('hidden')
    nav.style.width = '16em'
    article.style.marginLeft = '16em'
    yinyang.style.transform = 'rotate(540deg)'
    eclipse.style.display = 'block'
    document.body.style.overflowX = 'hidden'
    headerVisible(true)
  }
}

function headerVisible(flag) {
  // 首页导航条不隐藏
  if (/math\/example\/editor\.html$/.test(iframe.src)) {
    flag = true
  }
  if (!nav.hidden || flag) {
    header.removeAttribute('hidden')
    header.style.opacity = '1'
  } else {
    header.hidden = true
    header.style.opacity = '0'
  }
}

// 路由加载
const router = {
  toc: undefined,
  book: undefined,
  get (pos) {
    return this.toc[pos[0]].articles[pos[1]]
  },
  jump (src, title) {
    notify = new Notify('加载中...')
    createIframe(this.book.value + '/' + src, title)
    hideNav(true)
    setTimeout(headerVisible, 1000)
  },
  jumpPos (pos) {
    const item = this.get(pos)
    this.jump(item.src, item.title)

    document.querySelectorAll('#table-of-contents li').forEach(li => {
      const { href } = li.firstChild
      if (href && href.endsWith(item.src)) {
        li.classList.add('active')
      } else {
        li.classList.remove('active')
      }
    })
  },
  redirect (pos) {
    const src = this.get(pos).src.replace(/\.html$/, '')
    const hash = '#' + this.book.value + '/' + src
    window.history.pushState(null, null, hash)
    this.jumpPos(pos)
  },
  jumpto (pos) {
    return () => this.redirect(pos)
  },
  loadBook () {
    // a month
    function isRecent (dateStr) {
      if (!dateStr) return false
      return new Date() - new Date(dateStr) < 30 * 86400 * 1000
    }

    function preventJump (e) {
      e.preventDefault()
    }

    const {
      toc, title, color = 'yellow', favicon = 'pikachu.png'
    } = tocData[this.book.value]
    this.toc = toc
    document.body.id = 'body-' + color
    $('#favicon').href = 'img/' + favicon

    // render table of contents
    const latest = []
    const frag = $('', {}, this.toc.map((group, i) => {
      const ul = $('<ul>', {}, group.articles.map((article, j) => {
        const a = $('<a>', {
          innerText: article.title,
          href: this.book.value + '/' + this.toc[i].articles[j].src,
          onclick: preventJump
        });
        const isNew = isRecent(article.date)
        const child = $('<li>', {
          onclick: this.jumpto([i, j]),
          className: isNew ? ['isnew'] : []
        }, a);
        if (isNew) {
          const copy = child.cloneNode(true)
          // 为 copy 重新绑定事件
          copy.firstChild.onclick = preventJump
          copy.onclick = this.jumpto([i, j])
          copy.title = article.date
          latest.push(copy)
        }
        return child
      }))
      return $('<li>', group.title, ul)
    }))
    const latestUl = $('<ul>', {}, latest.sort((x, y) =>
      x.title > y.title ? -1 : 1
    ).slice(0, 10))
    frag.insertBefore(
      $('<li>', '最近', latestUl),
      frag.firstChild
    )
    const id = 'table-of-contents'
    const newtoc = $('<ul>', { id }, frag)
    nav.replaceChild(newtoc, $('#' + id))
  },
  getPos (src) {
    for (let i = 0; i < this.toc.length; ++i) {
      for (let j = 0; j < this.toc[i].articles.length; ++j) {
        if (this.toc[i].articles[j].src == src) {
          return [i, j]
        }
      }
    }
    return [0, 0]
  },
  changeBook (value) {
    if (value !== this.book.value) {
      this.book.value = value
      this.loadBook()
    }
  },
  parseHash() {
    let bookValue = 'math'
    let hash = location.hash
    if (hash) {
      hash = hash.slice(1)
      if (/^[^/]+\/[^/]+\/[^/]+$/.test(hash)) { // book/group/article
        const path = hash.split('/')
        const src = path[1] + '/' + path[2] + '.html'
        this.changeBook(path[0])
        return this.getPos(src)
      }
      if (tocData.hasOwnProperty(hash)) {
        bookValue = hash
      }
    }
    this.changeBook(bookValue)
    return [0, 0]
  },
  init () {
    $('#prev-article').onclick = () => {
      let [i, j] = this.parseHash()
      if (j === 0) {
        if (i === 0) return console.warn('already at first article.')
        j = this.toc[--i].articles.length
      }
      --j
      this.redirect([i, j])
    }

    $('#next-article').onclick = () => {
      let [i, j] = this.parseHash()
      if (j === this.toc[i].articles.length-1) {
        if (i === this.toc.length-1) return console.warn('no more articles.')
        ++i
        j = -1
      }
      ++j
      this.redirect([i, j])
    }

    window.onhashchange = () => {
      this.jumpPos(this.parseHash())
    }

    // toc.js 提供 tocData 对象
    const books = Object.keys(tocData).map(key => $('<option>', {
      innerText: tocData[key].title,
      value: key
    }))
    this.book = $('#book', {
      onchange () {
        router.loadBook()
        router.redirect([0, 0])
      }
    }, books)
    this.book.value = ''

    window.onhashchange()
  }
}

router.init()
})();
</script>
<!-- 站内搜索, 注入 dom #search-box #search-button -->
<script async src="https://js.sitesearch360.com/plugin/bundle/1027.js"></script>
</body>
</html>
